package org.dolphin.web.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.dolphin.entity.security.AccessControl;
import org.dolphin.entity.security.Url;
import org.dolphin.service.SecurityControlService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntPathRequestMatcher;
import org.springframework.security.web.util.RequestMatcher;

public class CustomFilterInvocationSecurityMetadataSource implements
    FilterInvocationSecurityMetadataSource, InitializingBean {
  private static final Logger LOGGER = LoggerFactory
      .getLogger(CustomFilterInvocationSecurityMetadataSource.class);
  @Autowired(required = true)
  private SecurityControlService securityControlService;
  protected Map<RequestMatcher, Collection<ConfigAttribute>> predefinedRequestMap;
  protected Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;

  public CustomFilterInvocationSecurityMetadataSource() {
  }

  public CustomFilterInvocationSecurityMetadataSource(
      LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap) {
    this.predefinedRequestMap = requestMap;
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    requestMap = new LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>>();
    List<Url> metadata = securityControlService.getSecurityMetadata();
    if (metadata != null) {
      for (Url url : metadata) {
        String requestUrl = url.getUrl();
        RequestMatcher requestMatcher = new AntPathRequestMatcher(requestUrl);
        List<AccessControl> accessControls = url.getAccessControls();
        if (accessControls != null && !accessControls.isEmpty()) {
          List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(accessControls.size());
          for (AccessControl control : accessControls) {
            String authority = control.getAuthority();
            attributes.add(new SecurityConfig(authority));
          }
          requestMap.put(requestMatcher, attributes);
        } else {
          LOGGER.warn("No access control found for url: " + requestUrl);
          continue;
        }
      }
    } else {
      LOGGER.warn("No Security metadata found");
    }
    if (predefinedRequestMap != null) {
      requestMap.putAll(predefinedRequestMap);
    } else {
      LOGGER.info("No predefined access control");
    }
  }

  @Override
  public Collection<ConfigAttribute> getAllConfigAttributes() {
    Set<ConfigAttribute> allAttributes = new HashSet<ConfigAttribute>();
    for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
      allAttributes.addAll(entry.getValue());
    }
    return allAttributes;
  }

  @Override
  public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
    final HttpServletRequest request = ((FilterInvocation) object).getRequest();
    for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap.entrySet()) {
      if (entry.getKey().matches(request)) {
        return entry.getValue();
      }
    }
    return null;
  }

  @Override
  public boolean supports(Class<?> clazz) {
    return FilterInvocation.class.isAssignableFrom(clazz);
  }
}
